package Monopol;

import ClientBL.Event;
import GameServer.eEventsID;
import Monopol.MonopolExceptions.*;
import Monopol.Events.MonopolyGameEvents;
import Player.Player;
import Square.City;
import Square.Negotiable;
import Square.Square;
import Utils.Utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Properties;
import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;
import monopolyServlet.MonopolyServlet;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 * 
 * @author assafmir
 */
public class MonopolyGame {

    private Boolean bUseAutoDice;
    private final Monopol monopol;
    private Integer playerID;
    private Timer gameEventsPolling;
    private String defPlayerName;
    private String defDiceMode;
    private String srvName;
    private Integer srvPort;
    private Cubes monopolCubes;
    public static final Integer BUY_ASSET_DIALOG_ID = 11284;
    public static final Integer BUY_HOUSE_DIALOG_ID = 14185;
    private static final Integer RESIGN_DIALOG_ID = 31083;
    private static final Integer DICES_DIALOG_ID = 5286;
    private Integer dice1 = new Integer(6);//contains the first dice
    private Integer dice2 = new Integer(6);//contains the second dice

    /**
     * The Cubes of the Game - 2 Cubes
     *
     * @author Avia Tam
     * @author Assaf Miron
     *
     */
    static public class Cubes {

	private Cube[] cubes;
	private boolean isDouble;

	public Cube[] getCubes() {
	    return cubes;
	}

	public boolean isDouble() {
	    return isDouble;
	}

	/**
	 * Sets the Cubes with Values
	 *
	 * @param arrCubes
	 *            The Cubes Integer Array
	 */
	public void setCubes(Integer cube1, Integer cube2) {
	    // Assuming that Cube 1 is in place 0
	    cubes[0] = new Cube(cube1);
	    // Assuming that Cube 1 is in place 1
	    cubes[1] = new Cube(cube2);
	    if (cube1 == cube2) {
		isDouble = true;
	    } else {
		isDouble = false;
	    }
	}

	public Cubes() {
	    super();
	    // Initialize the Cubes Array
	    cubes = new Cube[2];
	    // Initialize Each Cube
	    for (int i = 0; i < 2; i++) {
		cubes[i] = new Cube();
	    }
	}

	/**
	 * Rolls the Dices and updates if the result is a Double or not
	 *
	 * @return The Sum of both of the Cubes
	 */
	public Integer rollCubes() {
	    Integer num1, num2;
	    // Throw the Cubes
	    num1 = cubes[0].throwCube();
	    num2 = cubes[1].throwCube();

	    // Check if its a Double
	    if (num1 == num2) {
		isDouble = true;
	    } else {
		isDouble = false;
	    }

	    // Return the Cubes Result
	    return num1 + num2;
	}

	@Override
	public String toString() {
	    return cubes[0].getValue().toString() + ", "
		    + cubes[1].getValue().toString();
	}
    }

    public MonopolyGame() {
	try {
	    // Read the Configuration File
	    getConfigData();
	} catch (FileNotFoundException ex) {
	    Logger.getLogger(MonopolyGame.class.getName()).log(Level.SEVERE,
		    null, ex);
	}
	// Create a New Monopoly Object
	monopol = new Monopol();
	System.out.println("Initializing the Monopol Game...");
	// Initialize the Monopoly Game Board
	monopol.initGame(srvName, srvPort);
	// Create the Cubes
	monopolCubes = new Cubes();
    }

    /**
     * Reads (and Writes) The Configuration File Using the Properties Class
     */
    private void getConfigData() throws FileNotFoundException {
	// Get a new Properties Object
	Properties myConfig = new Properties();
	InputStream isConfig = null;
	try {
	    // Get the Application Configuration Resource as Stream
	    isConfig = getClass().getClassLoader().getResourceAsStream(
		    "resources/appConfig.xml");

	    // Check that the File Exists and There is a Stream to Use
	    if (isConfig != null) {
		// Load the Configurations from the Configuration XML
		myConfig.loadFromXML(isConfig);
		// Set the Properties to the Game
		srvName = myConfig.getProperty("ServerName", "localhost"); // Set
		// the
		// Server
		// Name
		srvPort = Integer.parseInt(myConfig.getProperty("ServerPort",
			"8080")); // Set the Server Port
		defPlayerName = myConfig.getProperty("DefaultPlayerName",
			"Player #"); // Set the Default Player Name
		defDiceMode = myConfig.getProperty("DefaultDiceRollMode",
			"Auto"); // Set the Default Dice Roll Mode
	    } // The Stream was Invalid
	    else {
		String resourcesPath = getClass().getClassLoader().getResource("resources").getPath().replace("file://", "");
		// Create a New File
		File newConfigFile = new File(resourcesPath + "/appConfig.xml");
		// Create the File
		newConfigFile.createNewFile();
		// Get the File Output Stream - For Writing
		OutputStream outConfig = new FileOutputStream(newConfigFile);
		// Set the Default Values to the Parameters
		srvName = "LocalHost";
		srvPort = 8080;
		defPlayerName = "Player #";
		defDiceMode = "Auto";
		// Save the Properties in the Properties Object
		myConfig.setProperty("ServerName", srvName);
		myConfig.setProperty("ServerPort", srvPort.toString());
		myConfig.setProperty("DefaultPlayerName", defPlayerName);
		myConfig.setProperty("DefaultDiceRollMode", defDiceMode);
		// Store the Properties in the AppConfig File
		myConfig.storeToXML(outConfig, "Monopoly Client Configuration");
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	}

    }

    /**
     * Initialize the Monopoly Gmae Squares
     */
    public void initGameSquares() {
	// Call the Monoply Square Init. Function
	monopol.initSquares();
    }

    /**
     * Get the Game Squares
     * @return @{link Square}
     *	    The Game Squares (as Array)
     */
    public Square[] getGameSquares() {
	return monopol.getSquares();
    }

    /**
     * Get the Status of the Squares in the Game
     * @return @{link Boolean}
     *	    True - If the Total Length of the Squares Array is 36
     *	    False - Otherwise
     */
    public Boolean getGameSquaresStatus() {
	return (monopol.getSquares().length == 36);
    }

    /**
     * Get the Status of the Players in The Game
     * @return @{link Boolean} 
     *	    True - If the Players Array is Initialized with Players
     *	    False - Otherwise
     */
    public Boolean getGamePlayersStatus() {
	Boolean retStatement = false;
	try {
	    if (!monopol.getPlayers().isEmpty()) {
		if (monopol.getPlayers().size() > 0) {
		    retStatement = true;
		} else {
		    retStatement = false;
		}
	    } else {
		retStatement = false;
	    }
	} catch (MonopolErrors me) {
	    me.printStackTrace();
	}
	return retStatement;

    }

    /**
     * Update the Players Array in the Game
     * @param gameName
     *	The Game name as it apears in the Server
     * @param totalNumOfPlayers
     *	The Total number of players in the game
     */
    public void updatePlayers(String gameName, Integer totalNumOfPlayers) {
	// Call the Monopoly Update Players Function
	monopol.updatePlayers(gameName, totalNumOfPlayers);
    }

    /**
     * Loads the Monopoly Game Opens a Monopoly Game Loader for choosing the
     * Players and Viewing the Active Games. This Function Loads the GUI Game
     * Board and Initializes it.
     */
    public void loadGame(String gameName, Integer humans, Integer computer, Boolean isAutoDice) {
	//  Initialize the Game Players
	monopol.loadGame(gameName, humans, computer, isAutoDice);
	// Show The Board
	try {
	} catch (Exception e) {
	    // Show the User the Error
	    Utils.showError(null,
		    "Error Initializing the Board\n" + e.getMessage());
	    // Print the Error Stack Trace
	    e.printStackTrace();
	    // Exit the Game
	    System.exit(1);
	}

    }

    /**
     * Start a new Monopoly Game Round. The Function Plays a New Monopoly Game
     * and Implements its Functionality
     */
    public void parseEventToJSON(Event evt) {

	String notify;// string that will contain any notification message
	// created in session
	String dialog;// string that will contain the dialog question that will pop
	String details;
	String assetState;
	Boolean dice;
	try {
	    // Verify that the Event is not Null
	    if (evt != null) {
		// Get the Event ID and Check wich Event is it
		eEventsID parseEvent = MonopolyGameEvents.getInstance().parseEvent(
			evt.getEventType());
		// Find the Player in the Game
		Player player = getPlayerByName(evt.getPlayerName().getValue());
		notify = "";// each entrance to switch creating an
		// empty string
		dice = false;// resets the dice value
		dialog = "";// each entrance to switch creating an
		// empty string
		// Switch between the Different Events
		switch (parseEvent) {
		    case GAME_START: {
			notify = "Welcome to the Game!";
			monopol.notificationStatusBuilder(notify, dice1, dice2);
			// Break and Restart
			break;
		    }
		    case AUTO_DICE_ROLL: {
			// The Dices were rolled - Save the Result
			dice1 = evt.getFirstDiceResult();
			dice2 = evt.getSecondDiceResult();
			// Set the Cubes in the Game
			monopolCubes.setCubes(dice1, dice2);
			// create JSON
			monopol.notificationStatusBuilder(notify, dice1, dice2);
			// Break and Restart
			break;
		    }
		    case GAME_OVER: {
			gameEnd();
			// create JSON
			monopol.notificationStatusBuilder("The Game is over", dice1, dice2);
			// Break and Restart
			break;
		    }
		    case GAME_WINNER: {
			notify = gameWinner(player);
			// create JSON
			monopol.notificationStatusBuilder(notify, dice1, dice2);
			// Break and Restart
			break;
		    }
		    case GET_OUT_JAIL: {
			if (player != null) {
			    // Notify other Players
			    notify = ("The Player " + player.getName() + " picked up the 'Get Out of Jail' Card.");
			    // Set the Player Get out of Prison Card
			    player.setRelease_from_prison_card(true);
			    // Set the Player as 'In Prison'
			    player.setIs_in_prison(false);
			    // create JSON
			    monopol.notificationStatusBuilder(notify, dice1, dice2);
			}
			// Break and Restart
			break;
		    }
		    case GO_TO_JAIL: {
			if (player != null) {
			    // Notify other Players
			    notify = ("The Player " + player.getName() + " Is sent to Prison");
			    // create JSON
			    monopol.notificationStatusBuilder(notify, dice1, dice2);
			    // Set the Player as 'In Prison'
			    player.setIs_in_prison(true);
			    // Break and Restart
			}
			break;
		    }
		    case ASSET_BOUGHT: {
			buyAsset(evt.getBoardSquareID(), player);
			// create JSON indicating asset was bought
			monopol.notificationStatusBuilder(
				player.getName()
				+ " bought "
				+ Monopol.getSquareName(evt.getBoardSquareID()), dice1, dice2);
			// Break and Restart
			break;
		    }
		    case HOUSE_BOUGHT: {
			buyHouse(evt.getBoardSquareID(), player);
			// create JSON indicating house was bought
			monopol.notificationStatusBuilder(
				player.getName()
				+ " bought a house on "
				+ Monopol.getSquareName(evt.getBoardSquareID()), dice1, dice2);
			// Break and Restart
			break;
		    }
		    case PLAYER_LOST: {
			if (player != null) {
			    notify = playerLost(player);
			}
			// create JSON
			monopol.notificationStatusBuilder(notify, dice1, dice2);
			// Break and Restart
			break;
		    }
		    case PLAYER_MOVE: {
			if (player != null) {
			    movingPlayer(player, monopolCubes.getCubes(),
				    evt.getBoardSquareID(),
				    evt.getNextBoardSquareID());
			}
			// create JSON
			monopol.notificationStatusBuilder(notify, dice1, dice2);
			// Break and Restart
			break;
		    }
		    case PLAYER_ON_START: {
			notify = (player.getName() + " is on the Start Square!\nYou will Get 400 Shekels!");
			// create JSON
			monopol.notificationStatusBuilder(notify, dice1, dice2);
			// Break and Restart
			break;
		    }
		    case PLAYER_PASS_START: {
			notify = ("You Passed the Start Square!\nYou will Get 200 Shekels!");
			// create JSON
			monopol.notificationStatusBuilder(notify, dice1, dice2);
			// Break and Restart
			break;
		    }
		    case PLAYER_PAY: {
			// Get the Message for The Player Payment
			notify = playerPayment(player, // Send the  Current  Player
				evt.getPaymentAmount(), // Set the  Payment  Amount
				evt.isPaymentToOrFromTreasury(), // Set  the  'Where'  To  Pay
				evt.isPaymemtFromUser(), // Set the Pay
				// from User
				evt.getPaymentToPlayerName().getValue()); // Set
			// the  Player  Name  to Pay  to  create JSON
			monopol.notificationStatusBuilder(notify, dice1, dice2);
			// Break and Restart
			break;
		    }
		    case PLAYER_RESIGN: {
			notify = playerQuits(player.getNumber());
			// create JSON
			monopol.notificationStatusBuilder(notify, dice1, dice2);
			// Break and Restart
			break;
		    }
		    case PLAYER_ROLL_DICE: {
			dice = true;
			// create JSON
			monopol.diceDialogBuilder(dice, DICES_DIALOG_ID, getPlayerName(player), dice1, dice2);
			// Break and Restart
			break;
		    }
		    case PLAYER_USED_GETOUTCARD: {
			if (player != null) {
			    // Notify that the Player used his Card
			    notify = ("The Player " + player.getName() + " Used the 'Get Out of Jail' Card.");
			    // create JSON
			    monopol.notificationStatusBuilder(notify, dice1, dice2);
			    // Set the Player Get out of Prison Card
			    player.setRelease_from_prison_card(false);
			    // Set the Player as 'In Prison'
			    player.setIs_in_prison(false);
			    // Break and Restart
			}
			break;
		    }
		    case PROMPT_BUY_ASSET: {
			if (player != null) {
			    // Find the Asset from the Square ID
			    Negotiable asset = monopol.getAsset(evt.getBoardSquareID());
			    // Check if the Player have enough Money to buy
			    if (asset.getCost() <= player.getFunds()) {
				dialog = "Do you want to buy " + asset.getName() + "?";
				details = (String) asset.getDetails();
				assetState = asset.getGroup().getName();
				// create JSON
				monopol.promptDialogBuilder(dialog, details, assetState, BUY_ASSET_DIALOG_ID, getPlayerName(player), dice1, dice2);
			    }
			}else{
			    System.out.println("ERROR: No Player!");
			}
			break;
		    }
		    case PROMPT_BUY_HOUSE: {
			if (player != null) {
			    // Find the Asset from the Square ID
			    City asset = (City) monopol.getAsset(evt.getBoardSquareID());
			    // Check if the Player have enough Money to buy
			    if (asset.getHouse_price() <= player.getFunds()) {
				dialog = "Do you want to buy a house in " + asset.getName() + "?";
				details = "House Price is " + asset.getHouse_price();
				assetState = asset.getGroup().getName();
				// create JSON
				monopol.promptDialogBuilder(dialog, details, assetState, BUY_HOUSE_DIALOG_ID, getPlayerName(player), dice1, dice2);
			    }
			}
			// Break and Restart
			break;
		    }
		    case SURPRISE_CARD: {
			// Show the Player a Surprise Card Message
			// Dialog
			notify = (evt.getEventMessage().getValue());
			// create JSON
			monopol.notificationStatusBuilder(notify, dice1, dice2);
			// Break and Restart
			break;
		    }
		    case WARNING_CARD: {
			// Show the Player a Wanrning Card Message
			// Dialog
			notify = (evt.getEventMessage().getValue());
			// create JSON
			monopol.notificationStatusBuilder(notify, dice1, dice2);
			// Break and Restart
			break;
		    }
		    default: {
			// Do Nothing...
			// Break and Restart
			break;
		    }
		}
	    } else {
		System.out.println("Error: Player was Null!");
	    }

	} catch (Exception e) {
	    e.printStackTrace();
	    // Check that the message is not Empty
	    if (e.getMessage() != null && !e.getMessage().isEmpty()) {
		// Show the Message to the User
		notify = e.getMessage();
	    }
	}
    }

    public void setPlayerCubes(Integer cube1, Integer cube2) {
	dice1 = cube1;
	dice2 = cube2;
    }

    /**
     * Returns the Player Unique Number
     * @param player
     *	The Player to Check
     * @return
     *	The Player ID in the Game
     */
    private String getPlayerName(Player player) {
	if (player != null) {
	    return player.getName();
	}
	return "[No Name]";
    }

    /**
     * End the Game
     */
    public void gameEnd() {
	// The Game is Over - Stop the Polling
	if (gameEventsPolling != null) {
	    System.out.println("DEBUG: Setting gameEnd");
	    MonopolyServlet.setGameEnd();
	    gameEventsPolling.cancel();
	}
    }

    /**
     * Finishes the Game with a Winner
     *
     * @param Player
     *            The Winning Player
     */
    public String gameWinner(Player player) {
	String notify = null;

	// The Game Ended - Stop the Polling
	if (gameEventsPolling != null) {
	    gameEventsPolling.cancel();
	}
	// The Last Player is the Winner - It will be the Only player left
	// in the Array List
	// Print Out the Winning Player
	notify = ("Congratulations to " + player.getName()
		+ " you are the winner" + "\nGame Over!");

	// Set the End of the Game at the Servlet
	MonopolyServlet.setGameEnd();

	return notify;
    }

    /**
     * Finds the Player in the Monopoly Game
     *
     * @param playerName
     *            The Player Name to Find
     * @return The {@link Player} Object
     */
    public Player getPlayerByName(String playerName) {
	return monopol.findPlayer(playerName);
    }

    /**
     * Buys an Asset in the Game
     *
     * @param squareID
     *            The Square ID to Buy (The Asset)
     * @param Player
     *            The Player who Buys the Asset
     */
    public void buyAsset(Integer squareID, Player player) {
	// Buy the Asset in the Game
	monopol.buyAsset(monopol.getAsset(squareID), player);
	// Update the Game GUI of the Changes
	// gameBoard.updatePlayer(Player, squareID);
    }

    /**
     * Buys a House on an Asset
     *
     * @param squareID
     *            The Square ID to Build the House On (The Asset)
     * @param Player
     *            The Player who buys the House
     */
    public void buyHouse(Integer squareID, Player player) {
	// Buy the House in the Game
	monopol.buyHouse((City) monopol.getAsset(squareID), player);
	// Add a House drawing on the Board
	// gameBoard.buyHouse(Player.getGame_location());
    }

    /**
     * Invokes when a Player Loses the Game
     *
     * @param Player
     *            The Player Name
     */
    public String playerLost(Player player) {
	String notify = null;
	try {
	    // Notify Other Players
	    notify = (player.getName() + " Lost the Game.");
	    // Delete the Player who Lost from the game
	    Boolean deletePlayer = Monopol.deletePlayer(player);
	    if (deletePlayer) {
		// Delete the Player from the Game
		// gameBoard.deletePlayer(Player.getNumber(),
		// Player.getGame_location());
		// Update All the Components in the Game Board
		// gameBoard.updateAll(Monopol.getCashier());
	    }
	} catch (MonopolErrors ex) {
	    notify = "Error on Player Losing the Game!\n"
		    + ex.getMessage();
	}
	return notify;
    }

    /**
     * Moving the Player in the Game
     *
     * @param Player
     *            The Player To Move
     * @param gameCubes
     *            The Total Game Cubes
     * @param currSquareID
     *            The Current Square Id the Player is On
     * @param nextSquareID
     *            The Next Square ID the Player needs to Be
     */
    public void movingPlayer(Player player, Cube[] gameCubes,
	    Integer currSquareID, Integer nextSquareID) {
	// Move the Player to the Next Square on the Board
	monopol.movePlayer(player, nextSquareID);
    }

    /**
     * Invokes when a Player needs to be Payed or Pay someone
     *
     * @param player
     *            The Player who needs Payments
     * @param paymentAmount
     *            The Amount of Payments
     * @param payToOrFromTreasury
     *            Is the Payment To / From the Treasury?
     * @param payFromUser
     *            Is the Payment from another Player?
     * @param payToPlayerName
     *            The Player name to pay to
     */
    public String playerPayment(Player player, Integer paymentAmount,
	    Boolean payToOrFromTreasury, Boolean payFromUser,
	    String payToPlayerName) {
	String notify = null;
	// Check if the Payment needs to be done to the Treasury or the Other
	// Players
	if (payToOrFromTreasury) {
	    // Check if the Player needs to Pay or Get Payed from Treasury
	    if (payFromUser) {
		// The Player needs to pay TO the Treasury
		monopol.setCashier(paymentAmount.doubleValue());
		player.addFunds(paymentAmount.doubleValue() * (-1));
		// Printout and notify the player of payment made
		notify = (player.getName() + " paid "
			+ paymentAmount.doubleValue() + " Shekels.");
	    } else {
		// The player needs to be payed FROM the Treasury
		monopol.setCashier(paymentAmount.doubleValue() * (-1));
		player.addFunds(paymentAmount.doubleValue());
		// Printout and notify the player of payment made
		notify = (player.getName() + " got "
			+ paymentAmount.doubleValue() + " Shekels.");
	    }
	} // The Player Needs to Pay or be Payed from Other Players
	else {
	    // Get the Player Object to Pay To
	    Player otherPlayer = monopol.findPlayer(payToPlayerName);
	    // Check that the Player Name to Pay to is Not Empty
	    if (!payToPlayerName.isEmpty() && otherPlayer != null) {
		// Check if the Payment needs to be done FROM the Player or TO
		// the Player
		if (payFromUser) {
		    // The Player Need TO Pay another Player
		    player.addFunds(paymentAmount.doubleValue() * (-1));
		    otherPlayer.addFunds(paymentAmount.doubleValue());
		    // Printout and notify the player of payment made
		    notify = (player.getName() + " paid "
			    + otherPlayer.getName() + " "
			    + paymentAmount.doubleValue() + " Shekels.");
		} else {
		    // The Player Gets Payed FROM another Player
		    player.addFunds(paymentAmount.doubleValue());
		    otherPlayer.addFunds(paymentAmount.doubleValue() * (-1));
		    // Printout and notify the player of payment made
		    notify = (otherPlayer.getName() + " paid "
			    + player.getName() + " "
			    + paymentAmount.doubleValue() + " Shekels.");
		}
	    } else {
		return "No Player Name to Pay to.";
	    }
	}
	return notify;
    }

    /**
     * Invokes when the Player Resigns from the Game
     *
     * @param Player
     *            The Player who Resigned
     */
    public String playerQuits(Integer playerID) {
	String o_notify;
	Player player = null;
	ArrayList<Player> players = null;
	try {
	    players = monopol.getPlayers();
	} catch (MonopolErrors ex) {
	    Logger.getLogger(MonopolyGame.class.getName()).log(Level.SEVERE,
		    null, ex);
	}
	for (int i = 0; i < players.size(); i++)// find player with the given id
	{
	    if (players.get(i).getNumber() == playerID) {
		player = players.get(i);
		i = players.size();
	    }
	}
	try {
	    // Notify Other Players
	    o_notify = ("The Player" + player.getName() + "Has Resigned from the Game.");
	    // Delete the Player who Resigned from the game
	    Boolean deletePlayer = Monopol.deletePlayer(player);
	    // Check if the Player is ready for Deletion
	    if (deletePlayer) {
		// Update the Server on the Resigned Player
		MonopolyGameEvents.getInstance().setResignedPlayer(
			player.getNumber());
	    }
	} catch (MonopolErrors ex) {
	    o_notify = "Error on Player Losing the Game!\n"
		    + ex.getMessage();
	}
	return o_notify;
    }

    /**
     * Sets the Dice Mode True - Using Automatic Dice Roll Mode False - Using
     * Manual Dice Roll Mode
     *
     * @param autoDice
     *            The Dice Mode
     */
    public void setAutomaticDiceMode(Boolean autoDice) {
	bUseAutoDice = autoDice;
    }

    /**
     * Return the Dice Mode True - Using Automatic Dice Roll Mode False - Using
     * Manual Dice Roll Mode
     *
     * @return The Dice Mode
     */
    public Boolean getAutomaticDiceMode() {
	return bUseAutoDice;
    }
}
